﻿using System;

using System.Collections.Generic;
using System.Text;
using Silvermoon.Shapes;
using System.Drawing;
using Silvermoon.Utils;

using Silvermoon.Animations;
using Silvermoon.OpenGL;
using Silvermoon.Images;
using Silvermoon.Windows;
using Silvermoon.Controls.Classes;
using Silvermoon.Controls.Base;
using Silvermoon.UI;

namespace Silvermoon.Controls
{
    public class ProgressBar : ChromeControl
    {
        const int borderSize = 2;
        const int ChangeDuration = 330;
        #region fields

        private IntAnimation animation;
        private BackgroundShape progressShape;
        private StringShape stringShape;
        private int value;
        private bool displayValue;
        private int minimum;
        private int maximum;

        #endregion
        #region ctor

        public ProgressBar()
            : base()
        {
            Color = PhoneColors.DeepBlue;
            maximum = 100;
            progressShape = new BackgroundShape
            {
                Color = Color,
                Style = RenderStyle.Metalic
            };
            Shapes.AddLast(progressShape);
            Size = new Size(100, 48);
            ControlFlags |= ControlFlags.CanPan;
        }

        protected override Shape CreateDefaultBackground()
        {
            return new ProgressBarShape();
        }



        protected override void Dispose(bool dispose)
        {
            AbortAnimation();
            if (stringShape != null && displayValue)
            {
                Shapes.Remove(stringShape);
                stringShape.Dispose();
                stringShape = null;
            }
            base.Dispose(dispose);
        }

        #endregion
        #region props

        /// <summary>
        /// Gets or sets whether to display the value as text inside the progress bar. The default value is false.
        /// </summary>
        public bool DisplayValue
        {
            get
            {
                return displayValue;
            }
            set
            {
                if (displayValue != value)
                {
                    displayValue = value;
                    Invalidate(InvalidationFlag.Appearance | InvalidationFlag.Custom);
                }
            }
        }


        /// <summary>
        /// Gets or sets the actual value which must be between Miniumum and Maximum.
        /// </summary>
        public int Value
        {
            get
            {
                return value;
            }
            set
            {
                value = ValidateValue(value);
                if (this.value != value)
                {
                    AbortAnimation();
                    OnValueChange(this.value, value);
                }
            }
        }

        /// <summary>
        /// Gets or sets the minimum value.
        /// </summary>
        public int Minimum
        {
            get
            {
                return minimum;
            }
            set
            {
                if (value != minimum)
                {
                    minimum = value;
                    Value = ValidateValue(this.value);
                    Invalidate(InvalidationFlag.Appearance);
                }
            }
        }

        /// <summary>
        /// Gets or sets the maximum value. The default value is 100.
        /// </summary>
        public int Maximum
        {
            get
            {
                return maximum;
            }
            set
            {
                if (value != maximum)
                {
                    maximum = value;
                    Value = ValidateValue(this.value);
                    Invalidate(InvalidationFlag.Appearance);
                }
            }
        }

        #endregion
        #region methods

        public override Rectangle ClientBounds
        {
            get
            {
                Rectangle r = new Rectangle(0, 0, Width, Height);
                r.Inflate(-borderSize, -borderSize);
                return r;
            }
        }

        protected virtual void OnValueChange(int oldValue, int newValue)
        {
            this.value = newValue;

            Invalidate(InvalidationFlag.Appearance);

            if (ValueChanged != null)
                ValueChanged(this, EventArgs.Empty);
        }

        private int ValidateValue(int value)
        {
            if (value < minimum)
                return minimum;
            if (value > maximum)
                return maximum;
            return value;
        }

        protected override void OnLayout()
        {
            if (CheckFlag(InvalidationFlag.Color))
            {
                progressShape.Color = Color;
            }
            if (CheckFlag(InvalidationFlag.Custom))
            {
                if (displayValue)
                {
                    if (stringShape == null)
                    {
                        stringShape = new StringShape
                            {
                                Alignment = Alignment.Center,
                                LineAlignment = Alignment.Center,
                                LineMode = LineMode.SingleLine,
                                Color = Color.White,
                                IsShadowed = true
                            };
                    }
                    Shapes.Append(progressShape, stringShape);
                }
                else
                {
                    if (stringShape != null)
                        Shapes.Remove(stringShape);
                }
            }
            if (CheckFlag(InvalidationFlag.Size | InvalidationFlag.Margin | InvalidationFlag.Appearance))
            {
                Rectangle r = ClientBounds;
                int size = maximum > 0 ? value * r.Width / maximum : 0;
                progressShape.Visible = size > 0;
                if (size > 0)
                {
                    r.Width = size;
                    progressShape.Bounds = Control.Scale(r);
                }
                if (displayValue)
                {
                    stringShape.Bounds = ClientBounds;
                    stringShape.Text = GetValueText();
                }

            }
            base.OnLayout();
        }

        private void SetValue(Point p)
        {
            int offset = p.X - ClientLeft;
            int w = Math.Max(1, ClientWidth);
            if (offset < 0)
                offset = 0;
            else if (offset > w)
                offset = w;
            this.Value = minimum + ((maximum - minimum) * offset / w);
        }

        protected virtual void AbortAnimation()
        {
            if (animation != null)
                animation.Abort();
        }

        /// <summary>
        /// Changes the value in a transition.
        /// </summary>
        /// <param name="value"></param>
        public void ChangeTo(int value)
        {
            value = ValidateValue(value);
            int d = Math.Abs(value - this.value);
            if (d > 4)
            {
                AbortAnimation();
                animation = new IntAnimation(ProgressBar.ChangeDuration, this.value, value, 0);
                animation.Progress += (s, e) => OnValueChange(this.value, e.Value);
                animation.InvokeAsync();
            }
            else
                Value = value;
        }

        protected virtual string GetValueText()
        {
            string text = value.ToString();
            if (GetDisplayText != null)
            {
                ProgressBarTextEventArgs e = new ProgressBarTextEventArgs
                {
                    Text = text
                };
                GetDisplayText(this, e);
                text = e.Text;
            }
            return text;
        }

        #endregion
        #region events

        public event EventHandler ValueChanged;

        public event EventHandler<ProgressBarTextEventArgs> GetDisplayText;

        #endregion
        #region IScrollEventBehavior Members


        internal protected override void OnBeginPan(PanEventArgs e)
        {
            // do not allow scrolling for this or any parented control:
            e.Handled = false;
            e.Handled = true;
        }

        #endregion

        public static readonly PropertyAccessor<ProgressBar,int> ValueProperty = PropertyAccessor.Register<ProgressBar,int>("Value", e => ((ProgressBar)e).value, (e, v) => ((ProgressBar)e).Value = v);

    }
}
